using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace Stratman.DesktopProcess.Ec2Console
{
    /// <summary>
    /// Data grid/input fields allowing the user to define and manage IP permissions defined for
    /// various groups.
    /// </summary>
    public partial class AuthorizationsForm : Form
    {
        /// <summary>
        /// Mappings between groups and IP permissions.
        /// </summary>
        private Dictionary<IpPermissionType, List<string>> authorizationMappings = new Dictionary<IpPermissionType, List<string>>();
        /// <summary>
        /// A complete list of defined IP permissions.
        /// </summary>
        private List<IpPermissionType> authorizations = new List<IpPermissionType>();
        /// <summary>
        /// The index of the row that was selected before the current SelectedIndexChanged event.
        /// </summary>
        private int previouslySelectedRow = -1;
        /// <summary>
        /// Flag indicating whether we are to suppress execution of the SelectedIndexChanged event
        /// handler.
        /// </summary>
        private bool suppressRowEventHandler = false;

        /// <summary>
        /// A complete list of defined IP permissions.
        /// </summary>
        public List<IpPermissionType> Authorizations
        {
            get
            {
                return authorizations;
            }
        }

        /// <summary>
        /// Mappings between groups and IP permissions.
        /// </summary>
        public Dictionary<IpPermissionType, List<string>> AuthorizationMappings
        {
            get
            {
                return authorizationMappings;
            }
        }

        /// <summary>
        /// Default constructor for the AuthorizationsForm class; populates the data grid, the
        /// list of IP permissions, and the mappings between IP permissions and groups.
        /// </summary>
        /// <param name="securityGroups">
        /// List of security groups (and their IP permissions) passed back to us by Amazon.
        /// </param>
        public AuthorizationsForm(DescribeSecurityGroupsResponseType securityGroups)
        {
            InitializeComponent();
            
            foreach (SecurityGroupItemType securityGroup in securityGroups.securityGroupInfo)
            {
                foreach (IpPermissionType authorization in securityGroup.ipPermissions)
                {
                    // Build the user-facing string of IP ranges for this particular row
                    StringBuilder ipRangeString = new StringBuilder();

                    for (int i = 0; i < authorization.ipRanges.Length; i++)
                    {
                        ipRangeString.Append(authorization.ipRanges[i].cidrIp);

                        if (i < authorization.ipRanges.Length - 1)
                            ipRangeString.Append(", ");
                    }

                    // If the permissions->groups mapping list doesn't contain this permission yet,
                    // add it to the list and to the data grid
                    if (!authorizationMappings.ContainsKey(authorization))
                    {
                        authorizationMappings.Add(authorization, new List<string>());
                        authorizationsDataGrid.Rows.Add(authorization.ipProtocol.ToUpper(), (authorization.fromPort == -1 ? "N/A" : (authorization.toPort == authorization.fromPort ? authorization.fromPort.ToString() : authorization.fromPort.ToString() + "-" + authorization.toPort.ToString())), ipRangeString.ToString());
                        authorizations.Add(authorization);
                    }

                    // Add a mapping between the group and the permission
                    authorizationMappings[authorization].Add(securityGroup.groupName);
                }

                securityGroupsListBox.Items.Add(securityGroup.groupName);
            }
        }

        /// <summary>
        /// Event handler that is called with the selected row in the data grid changes; populates
        /// the various fields with the information for the IP permission as well as the list of
        /// groups to which the permission is mapped.
        /// </summary>
        /// <param name="sender">
        /// Object from which this event originated (the data grid).
        /// </param>
        /// <param name="e">
        /// Arguments associated with the event.
        /// </param>
        private void authorizationsDataGrid_SelectionChanged(object sender, EventArgs e)
        {
            // Return immediately if we're not supposed to execute the event handler logic
            if (suppressRowEventHandler)
                return;

            // Save the information for the previously-selected row
            if (previouslySelectedRow != -1)
            {
                // Perform vetting on the information entered for the previous row; if it checks
                // out, then we continue executing, otherwise we select the previous row and stop
                // execution
                if (protocolDropdown.SelectedIndex == -1)
                {
                    suppressRowEventHandler = true;
                    authorizationsDataGrid.SelectedRows[0].Selected = false;
                    authorizationsDataGrid.Rows[previouslySelectedRow].Selected = true;
                    MessageBox.Show("Please select a protocol.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    suppressRowEventHandler = false;

                    return;
                }

                if (protocolDropdown.Text != "ICMP" && (!Regex.IsMatch(startPortTextBox.Text, "^\\d+$") || (endPortTextBox.Text != "" && !Regex.IsMatch(endPortTextBox.Text, "^\\d+$"))))
                {
                    suppressRowEventHandler = true;
                    authorizationsDataGrid.SelectedRows[0].Selected = false;
                    authorizationsDataGrid.Rows[previouslySelectedRow].Selected = true;
                    MessageBox.Show("Please enter a valid port or port range.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    suppressRowEventHandler = false;

                    return;
                }

                if (securityGroupsListBox.CheckedItems.Count == 0)
                {
                    suppressRowEventHandler = true;
                    authorizationsDataGrid.SelectedRows[0].Selected = false;
                    authorizationsDataGrid.Rows[previouslySelectedRow].Selected = true;
                    MessageBox.Show("Please select at least one security group.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    suppressRowEventHandler = false;

                    return;
                }

                authorizationMappings.Remove(authorizations[previouslySelectedRow]);

                // Set the properties for the IP permission to what was entered in the text boxes
                authorizations[previouslySelectedRow].fromPort = (startPortTextBox.Text == "" ? -1 : Convert.ToInt32(startPortTextBox.Text));
                authorizations[previouslySelectedRow].toPort = (endPortTextBox.Text == "" ? (startPortTextBox.Enabled ? Convert.ToInt32(startPortTextBox.Text) : -1) : Convert.ToInt32(endPortTextBox.Text));
                authorizations[previouslySelectedRow].ipProtocol = protocolDropdown.Text.ToLower();
                authorizations[previouslySelectedRow].ipRanges = new IpRangeItemType[ipRangesListBox.Items.Count];

                // Build the user-facing string of IP ranges for this particular row
                StringBuilder ipRangesString = new StringBuilder();

                for (int i = 0; i < ipRangesListBox.Items.Count; i++)
                {
                    authorizations[previouslySelectedRow].ipRanges[i] = new IpRangeItemType();
                    authorizations[previouslySelectedRow].ipRanges[i].cidrIp = (string)ipRangesListBox.Items[i];
                    
                    ipRangesString.Append((string)ipRangesListBox.Items[i]);

                    if (i < ipRangesListBox.Items.Count - 1)
                        ipRangesString.Append(", ");
                }

                // Add the IP permission->group mappings
                authorizationMappings.Add(authorizations[previouslySelectedRow], new List<string>());

                foreach (object securityGroup in securityGroupsListBox.CheckedItems)
                    authorizationMappings[authorizations[previouslySelectedRow]].Add((string)securityGroup);

                // Set the cells in the data grid to the values entered in the fields
                authorizationsDataGrid.Rows[previouslySelectedRow].Cells["Protocol"].Value = protocolDropdown.Text;
                authorizationsDataGrid.Rows[previouslySelectedRow].Cells["Ports"].Value = (startPortTextBox.Text == "" ? "N/A" : (startPortTextBox.Text == endPortTextBox.Text || endPortTextBox.Text == "" ? startPortTextBox.Text : startPortTextBox.Text + "-" + endPortTextBox.Text));
                authorizationsDataGrid.Rows[previouslySelectedRow].Cells["IPRanges"].Value = ipRangesString.ToString();
            }

            if (authorizationsDataGrid.SelectedRows.Count == 0)
                return;

            IpPermissionType authorization = authorizations[authorizationsDataGrid.SelectedRows[0].Index];

            // Set the values of the fields to those of the properties for the currently-selected
            // row
            protocolDropdown.Text = (String.IsNullOrEmpty(authorization.ipProtocol) ? "" : authorization.ipProtocol.ToUpper());

            if (authorization.ipProtocol != null && authorization.ipProtocol.ToUpper() == "ICMP")
            {
                startPortTextBox.Text = "";
                endPortTextBox.Text = "";
                startPortTextBox.Enabled = false;
                endPortTextBox.Enabled = false;
            }

            else if (authorization.fromPort >= 0)
            {
                startPortTextBox.Text = authorization.fromPort.ToString();
                endPortTextBox.Text = (authorization.toPort == authorization.fromPort || authorization.toPort == 0 ? "" : authorization.toPort.ToString());
                startPortTextBox.Enabled = true;
                endPortTextBox.Enabled = true;
            }

            else
            {
                startPortTextBox.Text = "";
                endPortTextBox.Text = "";
            }

            ipRangesListBox.Items.Clear();

            // Add each IP range to the list box
            if (authorization.ipRanges != null)
            {
                foreach (IpRangeItemType ipRange in authorization.ipRanges)
                    ipRangesListBox.Items.Add(ipRange.cidrIp);
            }

            // Set the checked property appropriately for each group
            for (int i = 0; i < securityGroupsListBox.Items.Count; i++)
            {
                securityGroupsListBox.SetItemChecked(i, false);

                foreach (string securityGroup in authorizationMappings[authorization])
                {
                    if ((string)securityGroupsListBox.Items[i] == securityGroup)
                        securityGroupsListBox.SetItemChecked(i, true);
                }
            }    

            ipTextBox1.Text = "";
            ipTextBox2.Text = "";
            ipTextBox3.Text = "";
            ipTextBox4.Text = "";
            ipMaskTextBox.Text = "";

            previouslySelectedRow = authorizationsDataGrid.SelectedRows[0].Index;
        }

        /// <summary>
        /// Event handler for when the cancel button is clicked; sets the DialogResult property
        /// appropriately and closes the form.
        /// </summary>
        /// <param name="sender">
        /// Object from which this event originated (the form).
        /// </param>
        /// <param name="e">
        /// Arguments associated with the event.
        /// </param>
        private void cancelButton_Click(object sender, EventArgs e)
        {
            DialogResult = DialogResult.Cancel;
            Close();
        }

        /// <summary>
        /// Event handler for when a new row is selected in the IP range list box; sets the values
        /// of the IP/netmask text boxes appropriately.
        /// </summary>
        /// <param name="sender">
        /// Object from which this event originated (the IP range list box).
        /// </param>
        /// <param name="e">
        /// Arguments associated with the event.
        /// </param>
        private void ipRangesListBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (ipRangesListBox.SelectedIndex == -1)
                return;

            // Split the IP/mask string into its component parts
            string ipRange = (string)ipRangesListBox.SelectedItem;
            string ip = ipRange.Split('/')[0];
            string netMask = ipRange.Split('/')[1];
            string[] ipBytes = ip.Split('.');

            // Set the value of each text box appropriately
            ipMaskTextBox.Text = netMask;
            ipTextBox1.Text = ipBytes[0];
            ipTextBox2.Text = ipBytes[1];
            ipTextBox3.Text = ipBytes[2];
            ipTextBox4.Text = ipBytes[3];
        }

        /// <summary>
        /// Event handler for when the IP range cancel button is clicked; discards any currently-
        /// entered information.
        /// </summary>
        /// <param name="sender">
        /// Object from which this event originated (the IP range cancel button).
        /// </param>
        /// <param name="e">
        /// Arguments associated with the event.
        /// </param>
        private void ipAddressCancelButton_Click(object sender, EventArgs e)
        {
            ipRangesListBox_SelectedIndexChanged(null, null);
        }

        /// <summary>
        /// Event handler for when the IP range OK button is clicked; validates and saves the
        /// information entered by the user.
        /// </summary>
        /// <param name="sender">
        /// Object from which this event originated (the IP range OK button).
        /// </param>
        /// <param name="e">
        /// Arguments associated with the event.
        /// </param>
        private void ipAddressOKButton_Click(object sender, EventArgs e)
        {
            if (ipRangesListBox.SelectedIndex != -1)
            {
                string ipRange = ipTextBox1.Text + "." + ipTextBox2.Text + "." + ipTextBox3.Text + "." + ipTextBox4.Text + "/" + ipMaskTextBox.Text;

                // Make sure that the user entered a valid IP address/netmask combination
                if (!Regex.IsMatch(ipRange, "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))/(?:3[0-2]{1}|[1-2]{1}[0-9]{1}|[0-9]{1})$"))
                {
                    MessageBox.Show("IP address/mask must be in XXX.XXX.XXX.XXX/XX format", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                ipRangesListBox.Items[ipRangesListBox.SelectedIndex] = ipRange;
            }
        }

        /// <summary>
        /// Event handler for when the add IP range button is clicked; adds a new entry to the
        /// list box.
        /// </summary>
        /// <param name="sender">
        /// Object from which this event originated (the add IP range button).
        /// </param>
        /// <param name="e">
        /// Arguments associated with the event.
        /// </param>
        private void addIPRangeButton_Click(object sender, EventArgs e)
        {
            ipRangesListBox.Items.Add("0.0.0.0/0");
            ipRangesListBox.SelectedIndex = ipRangesListBox.Items.Count - 1;
        }

        /// <summary>
        /// Event handler for when the delete IP range button is clicked; removes the currently-
        /// selected entry from the IP range list box.
        /// </summary>
        /// <param name="sender">
        /// Object from which this event originated (the delete IP range button).
        /// </param>
        /// <param name="e">
        /// Arguments associated with the event.
        /// </param>
        private void deleteIPRangeButton_Click(object sender, EventArgs e)
        {
            if (ipRangesListBox.SelectedIndex != -1)
                ipRangesListBox.Items.RemoveAt(ipRangesListBox.SelectedIndex);
        }

        /// <summary>
        /// Event handler for when the delete IP permission button is clicked; removes the
        /// currently-selected row from the data grid and its corresponding information from the
        /// permissions and group mapping lists.
        /// </summary>
        /// <param name="sender">
        /// Object from which this event originated (the delete IP permission button).
        /// </param>
        /// <param name="e">
        /// Arguments associated with the event.
        /// </param>
        private void deleteButton_Click(object sender, EventArgs e)
        {
            if (previouslySelectedRow == -1)
                return;

            authorizationMappings.Remove(authorizations[previouslySelectedRow]);
            authorizations.RemoveAt(previouslySelectedRow);
            previouslySelectedRow = -1;

            authorizationsDataGrid.Rows.RemoveAt(authorizationsDataGrid.SelectedRows[0].Index);
        }

        /// <summary>
        /// Event handler for when the selected index in the protocol dropdown changes; enables
        /// (when the protocol is TCP or UDP) or disables (when the protocol is ICMP) the port
        /// range text boxes.
        /// </summary>
        /// <param name="sender">
        /// Object from which this event originated (the protocol dropdown).
        /// </param>
        /// <param name="e">
        /// Arguments associated with the event.
        /// </param>
        private void protocolDropdown_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (protocolDropdown.Text == "ICMP")
            {
                startPortTextBox.Text = "";
                startPortTextBox.Enabled = false;
                endPortTextBox.Text = "";
                endPortTextBox.Enabled = false;
            }

            else
            {
                startPortTextBox.Enabled = true;
                endPortTextBox.Enabled = true;
            }
        }

        /// <summary>
        /// Event handler for when the create new IP permission button is clicked; adds a new row
        /// to the data grid and new entries to the various list variables.
        /// </summary>
        /// <param name="sender">
        /// Object from which this event originated (the add IP permission button).
        /// </param>
        /// <param name="e">
        /// Arguments associated with the event.
        /// </param>
        private void createNewButton_Click(object sender, EventArgs e)
        {
            authorizationsDataGrid_SelectionChanged(null, null);

            // Create a new IP permission object and add a row to the data grid
            authorizations.Add(new IpPermissionType());
            authorizations[authorizations.Count - 1].fromPort = -1;
            authorizationMappings.Add(authorizations[authorizations.Count - 1], new List<string>());
            authorizationsDataGrid.Rows.Add();
            authorizationsDataGrid.SelectedRows[0].Selected = false;
            authorizationsDataGrid.Rows[authorizationsDataGrid.Rows.Count - 1].Selected = true;
        }

        /// <summary>
        /// Event handler for when the OK button is clicked; sets the DialogResult property
        /// appropriately and closes the form.
        /// </summary>
        /// <param name="sender">
        /// Object from which this event originated (the form).
        /// </param>
        /// <param name="e">
        /// Arguments associated with the event.
        /// </param>
        private void okButton_Click(object sender, EventArgs e)
        {
            // Fire the selection changed event handler to save any information in the IP 
            // permission fields
            authorizationsDataGrid_SelectionChanged(null, null);

            DialogResult = DialogResult.OK;
            Close();
        }
    }
}